/**
 * <p>Title: SecurityConfiguration.java</p>
 * <p>Description: 用户访问控制</p>
 * <p>Copyright: Copyright (c) 2019</p>
 * <p>Company: 深圳市中钞信达成都研发中心</p>
 *
 * @author noel
 * @date 2019年9月6日
 */
package com.song.config;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.song.config.security.JwtAuthenticationEntryPoint;
import com.song.config.security.JwtAuthenticationTokenFilter;
import com.song.dao.IUser;
import com.song.dao.Result;
import com.song.pojo.ResultMsg;
import com.song.util.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * 用户访问控制
 *
 * @author noel
 * @date 2019年9月6日
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomUserDetailsService userDetailsService;
    @Resource
    private IUser iUser;
    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private ObjectMapper objectMapper;

    private final JwtAuthenticationEntryPoint unauthorizedHandler;

    private final AccessDeniedHandler accessDeniedHandler;

    private final UserDetailsService CustomUserDetailsService;

    private final JwtAuthenticationTokenFilter authenticationTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {


        // TODO Auto-generated method stub
        http.httpBasic()
                .authenticationEntryPoint((request, response, authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("status", 1);
                    map.put("msg", "未登录");
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/login",
                        "/user/register",
                        "/user/getlogininfo",
                        "/**"
                ).permitAll()

                .anyRequest().authenticated()

                .and().formLogin().permitAll()
                //登录失败，返回json
                .failureHandler((request, response, ex) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    PrintWriter out = response.getWriter();
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("status", 1);
                    if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
                        map.put("msg", "用户名或密码错误");
                    } else if (ex instanceof DisabledException) {
                        map.put("msg", "账户被禁用");
                    } else {
                        map.put("msg", "登录失败!");
                    }
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                //登录成功，返回json
                .successHandler((request, response, authentication) -> {
                    UsernamePasswordAuthenticationToken details = (UsernamePasswordAuthenticationToken) authentication;
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("status", 0);
                    CustomUserDetails principal = (CustomUserDetails) details.getPrincipal();
                    String token = jwtUtils.generateAccessToken(principal);
                    principal.setToken(token);
                    principal.setPassword("就不告诉你密码！！");
                    map.put("msg", principal);
                    response.setContentType("application/json;charset=utf-8");
                    response.setHeader("token", token);
                    jwtUtils.putToken(principal.getUsername(),token);
                    PrintWriter out = response.getWriter();
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .and()
                .exceptionHandling()
                //没有权限，返回json
                .accessDeniedHandler((request, response, ex) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("status", 1);
                    map.put("msg", "权限不足");
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .and().logout()
                //退出成功，返回json
                .logoutSuccessHandler((request, response, authentication) -> {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("status", 1);
                    map.put("msg", "退出成功");
                    map.put("data", authentication);
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                }).deleteCookies("JSESSIONID")
                .permitAll()
                .and().rememberMe().rememberMeParameter("remember-me")
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().csrf().disable();
        http.cors();
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        http.authenticationProvider(new QQLoginProvider()).addFilterAt(qqLoginFilter(), AbstractPreAuthenticatedProcessingFilter.class);
        http.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);

    }


    @Autowired
    public SecurityConfiguration(JwtAuthenticationEntryPoint unauthorizedHandler,
                                 @Qualifier("RestAuthenticationAccessDeniedHandler") AccessDeniedHandler accessDeniedHandler,
                                 UserDetailsService CustomUserDetailsService,
                                 JwtAuthenticationTokenFilter authenticationTokenFilter) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.accessDeniedHandler = accessDeniedHandler;
        this.CustomUserDetailsService = CustomUserDetailsService;
        this.authenticationTokenFilter = authenticationTokenFilter;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    QQLoginFilter qqLoginFilter() throws Exception {
        QQLoginFilter filter = new QQLoginFilter(iUser);
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");

                QQLoginToken qqLoginToken = (QQLoginToken) authentication;
                ResultMsg resultMsg = null;
                if (qqLoginToken.getType() == 0) {
                    resultMsg = Result.error(1, "授权登录失败");
                } else if (qqLoginToken.getType() == 1) {
                    resultMsg = Result.success(qqLoginToken);
                } else if (qqLoginToken.getType() == 2) {
                    resultMsg = Result.error(2, "绑定账号");
                }
                httpServletResponse.getWriter().write(JSON.toJSONString(resultMsg));

            }
        });
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().write(JSON.toJSONString(Result.error(1, "登录失败!")));
            }
        });
        return filter;
    }


}